#include "stdafx.h"
#include "CombatSysImp.h"
#include "CombatSkill.h"
#include "Combat/EvtCombat.h"
#include "Move/EvtMove.h"
#include "Entity/EntityIf.h"
#include "Entity/EvtEntity.h"
#include "Scene/SceneIf.h"


ICombatCalculator*	CombatSysImp::s_pCurDefaultCalculator = NULL;

CombatSysImp::CombatSysImp():
m_bInited(false),
m_theCbtSt(Combat::CBS_Peace),

m_nTargetCC(0), 
m_nTargetCE(0),
m_wpCastingSkill(NULL),
m_nSR_SpellcastingBaseForCls(0),
m_nSR_SpellcastedBaseForCls(0),
m_nSR_SpellcastingBaseForElement(0),
m_nSR_SpellcastingBaseForUnderAttack(0),
m_nPrevCbtSt(Combat::CBS_Peace),
m_nSequenceCC(0),
m_nSequenceGen(0),
sHP(0), sMaxHP(0), sMP(0), sMaxMP(0)
{
	for (int i= Combat::CBS_Begin+1; i<Combat::CBS_End; i++)
	{
		m_theStates[i] = NULL;
	}


	memset(m_nPropsModifier, 0, sizeof(m_nPropsModifier));
	memset(m_nProps, 0, sizeof(m_nProps));
}

CombatSysImp::~CombatSysImp()
{
	for (int i= Combat::CBS_Begin+1; i<Combat::CBS_End; i++)
	{
		if (m_theStates[i] != NULL)
		{
			delete m_theStates[i];
		}
	}
}

//
//	set gene host
bool 
CombatSysImp::setHost(IGeneHost* pHost)
{
	m_wpHost = dynamic_cast<IEntity*>(pHost);
	return (m_wpHost!=NULL);
}


void
CombatSysImp::setExtData(short nIdx, const int& nValue /* = 0 */)
{
	//
	//	Not Implement Yet
}

int
CombatSysImp::getExtData(short nIdx, const int& nValue /* = 0 */)
{
	//
	//	Not Implement Yet
	return nValue;
}


void 
CombatSysImp::setFlg(const short& idx)
{
	m_flags.setFlg(idx);
}

void
CombatSysImp::clrFlg(const short& idx)
{
	m_flags.clrFlg(idx);
}

bool
CombatSysImp::hasFlg(const short& idx)const
{
	return m_flags.hasFlg(idx);
}

void CombatSysImp::setState(short st)
{
#ifdef _DEBUG
	if (st>Combat::CBS_Begin && st<Combat::CBS_End)
	{
		LOG_DEBUG_V("ICombatSys%s::setState[%s->%s]\n", getCombatTag() , getStateName(m_theCbtSt), getStateName(st));
		m_theCbtSt = st;
	}
	else
	{
		LOG_ERROR("Failed to set combat state, invalid state!\n");
	}
#else
	if (st>Combat::CBS_Begin && st<Combat::CBS_End)
	{
		m_theCbtSt = st;
	}
	else
	{
		LOG_ERROR("Failed to set combat state, invalid state!\n");
	}
#endif
}

short CombatSysImp::getState()const
{
	return m_theCbtSt;
}

const char* CombatSysImp::getStateName()const
{
	return getStateName(m_theCbtSt);
}

const char* CombatSysImp::getStateName(short st)const
{
	static char* _stat_names[] =
	{
		"CBS_Peace",
		"CBS_Ready",
		"CBS_Casting",
		"CBS_CoolDown",
		"CBS_Forbidden",
		"CBS_Charging",
		"CBS_Standby",
	};

	if (st>Combat::CBS_Begin && st<Combat::CBS_End)
	{
		return _stat_names[st];
	}
	else
	{
		return "CBS_Unknow";
	}
}

void CombatSysImp::setFaction(short f)
{
	m_nFaction = f;
}

short CombatSysImp::getFaction()const
{
	return m_nFaction;
}

const char* CombatSysImp::getFactionName()const
{
	static char* _faction_names[] =
	{
		"Faction_Unknow"
		"Faction_Player",
		"Faction_Monster",
	};

	if (m_nFaction>=0 && m_nFaction<Combat::FC_Max)
	{
		return _faction_names[m_nFaction];
	}
	else
	{
		return "Faction_Unknow";
	}
}

void CombatSysImp::calculate(ICombatCalculator* pCalculator)
{
	if (s_pCurDefaultCalculator)
	{
		delete s_pCurDefaultCalculator;
	}

	s_pCurDefaultCalculator = pCalculator;
}

bool CombatSysImp::calculate(CombatSkill& ab, IEntity& c, IEntity& t, CombatResult& r, ICombatCalculator* pOverrideCalculator)
{
	ICombatSys* cbtc = c.getCombatSys();
	ICombatSys* cbtt = t.getCombatSys();
	if (cbtc && cbtt)
	{
		if (pOverrideCalculator)
		{
			return pOverrideCalculator->doCalc(ab, (*cbtc), (*cbtt), r);
		}
		else if (s_pCurDefaultCalculator)
		{
			return s_pCurDefaultCalculator->doCalc(ab, (*cbtc), (*cbtt), r);
		}
	}
	return false;
}

void 
CombatSysImp::setHP(int val)
{
	sHP = val;
}

void 
CombatSysImp::setMaxHP(int val)
{
	sMaxHP = val;
}

void
CombatSysImp::setMP(int val)
{
	sMP = val;
}

void
CombatSysImp::setMaxMP(int val)
{
	sMaxMP = val;
}


int 
CombatSysImp::getHP()const
{
	return int(sHP);
}

int 
CombatSysImp::getMaxHP()const
{
	return int(sMaxHP);
}

int
CombatSysImp::getMP()const
{
	return int(sMP);
}

int
CombatSysImp::getMaxMP()const
{
	return int(sMaxMP);
}

int
CombatSysImp::getHPPercentage()const
{
	int nPercent = 100;
	if (sMaxHP>0)
	{
		nPercent =  100 * sHP / sMaxHP; 
	}
	return nPercent;
}

int 
CombatSysImp::getMPPercentage()const
{
	int nPercent = nPercent =  100 * sMP / 100; 
	return nPercent;
}

int 
CombatSysImp::getBottleHP()const
{
	return int(sHPBottleValue);
}

int
CombatSysImp::getBottleMP()const
{
	return int(sMPBottleValue);
}

int	CombatSysImp::getHpBottleNum()const
{
	return (int)((float)sHPBottleValue/COMBAT_ONE_BOTTLE_HP_VALUE) ;
}

int CombatSysImp::getMpBottleNum()const
{
	return (int) ((float)sMPBottleValue/COMBAT_ONE_BOTTLE_ENERGY_VALUE) ;
}



void
CombatSysImp::updateHPByPercentage(int nPercent)
{
	int nAffectValue = sMaxHP * nPercent / 100;

	if (nAffectValue==0)
	{
		if (nPercent>0)
		{
			nAffectValue = 1;
		}
		else if (nPercent<0)
		{
			nAffectValue = -1;
		}
	}

	sHP += nAffectValue;

	if (sHP>sMaxHP)
	{
		sHP = sMaxHP;
	}
	else if(sHP<0)
	{
		sHP = 0;
	}

	if (m_wpHost)
	{
		EvtEntityPC evtx(EvtEntityPC::eProp_CombatHP, nAffectValue, sHP, sMaxHP);
		m_wpHost->onEvent(evtx);
	}
}

void CombatSysImp::updateHPBottleValue(int nAffectValue)
{
	if (nAffectValue!=0)
	{
		sHPBottleValue += nAffectValue;
		if (sHPBottleValue<0)
		{
			sHPBottleValue = 0;
		}

		if (m_wpHost)
		{
			EvtEntityPC evtx(EvtEntityPC::eProp_CombatHPBottle, nAffectValue);
			m_wpHost->onEvent(evtx);
		}
	}
}

void CombatSysImp::updateMPBottleValue(int nAffectValue)
{
	if (nAffectValue!=0)
	{
		sMPBottleValue += nAffectValue;
		if (sMPBottleValue<0)
		{
			sMPBottleValue = 0;
		}

		if (m_wpHost)
		{
			EvtEntityPC evtx(EvtEntityPC::eProp_CombatMPBottle, nAffectValue);
			m_wpHost->onEvent(evtx);
		}
	}
}

void
CombatSysImp::updateHP(int nAffectValue)
{
	if (nAffectValue!=0)
	{
		sHP += nAffectValue;
		if (sHP>sMaxHP)
		{
			sHP = sMaxHP;
		}
		else if (sHP<0)
		{
			sHP = 0;
		}

		if (m_wpHost)
		{
			EvtEntityPC evtx(EvtEntityPC::eProp_CombatHP, nAffectValue, sHP, sMaxHP);
			m_wpHost->onEvent(evtx);
		}
	}
}

void
CombatSysImp::updateMP(int nAffectValue)
{
	if (nAffectValue!=0)
	{
		sMP += nAffectValue;
		if (sMP>100)
		{
			sMP = 100;
		}
		else if (sMP<0)
		{
			sMP = 0;
		}
		
		if (m_wpHost)
		{
			EvtEntityPC evtx(EvtEntityPC::eProp_CombatMP, nAffectValue, sMP, 100);
			m_wpHost->onEvent(evtx);
		}
	}
}

void
CombatSysImp::updateMaxHP(int nAffectValue)
{
	if (nAffectValue>0)
	{
		sMaxHP += nAffectValue;
		sHP += nAffectValue;
		
		if (m_wpHost)
		{
			EvtEntityPC evtx(EvtEntityPC::eProp_CombatHP, nAffectValue, sHP, sMaxHP);
			m_wpHost->onEvent(evtx);
		}
	}
	else if (nAffectValue<0)
	{
		sMaxHP += nAffectValue;
		if (sMaxHP<0)
		{
			sMaxHP = 1;
		}
		if (sHP>sMaxHP)
		{
			sHP = sMaxHP;
		}
		
		if (m_wpHost)
		{
			EvtEntityPC evtx(EvtEntityPC::eProp_CombatHP, nAffectValue, sHP, sMaxHP);
			m_wpHost->onEvent(evtx);
		}
	}
}

void CombatSysImp::setProps(short nIdx, const int& nValue /* = 0 */)
{
	m_nProps[nIdx] = nValue;
}

int	CombatSysImp::getProps(short nIdx)const
{
	return m_nProps[nIdx];
}

void CombatSysImp::setPropsModifier(short nIdx, const int& nValue /* = 0 */)
{
	m_nPropsModifier[nIdx] = nValue;
}

int	CombatSysImp::getPropsModifier(short nIdx)const
{
	return m_nPropsModifier[nIdx];
}


bool CombatSysImp::addImmunity(int nKey)
{
	ImmunityMap::iterator it = m_theImmunityMap.find(nKey);
	if (it==m_theImmunityMap.end())
	{
		m_theImmunityMap[nKey] = 1;
	}
	else
	{
		it->second += 1;
	}
	return true;
}

bool CombatSysImp::rmvImmunity(int nKey)
{
	ImmunityMap::iterator it = m_theImmunityMap.find(nKey);
	if (it==m_theImmunityMap.end())
	{
		return false;
	}
	else
	{
		it->second -= 1;

		if (it->second<0)
		{
			it->second = 0;
			return false;
		}
	}
	return true;
}

bool CombatSysImp::hasImmunity(int nKey)const
{
	ImmunityMap::const_iterator it = m_theImmunityMap.find(nKey);
	if (it==m_theImmunityMap.end())
	{
		return false;
	}
	else
	{
		return it->second>0;
	}
}


static char __cType_[2][2] = {{3,1},{0,2}}; 

bool 
CombatSysImp::sendMsg_CbtTargt(IEntity& t)
{
	return true;
}

bool 
CombatSysImp::sendMsg_CbtStart(IEntity& t, CombatSkill& ab)
{
	return false;
}

bool 
CombatSysImp::sendMsg_CbtEnd()
{
	return false;
}

bool
CombatSysImp::sendMsg_CbtCancel()
{
	return false;
}

int		
CombatSysImp::getID()const
{
	return 0;
}

int		
CombatSysImp::getMutexRule()const
{
	return 0;
}

int
CombatSysImp::getMutexGroup()const
{
	return 0;
}

int		
CombatSysImp::getDuration()const
{
	return 0;
}

IData*	
CombatSysImp::getStaticData()
{
	return NULL;
}

bool 
CombatSysImp::onInit(IGeneCreateData& createData)
{
	return true;
}


bool
CombatSysImp::onAttach()
{
	m_wpHost->addRegEvt(Event::GID_EntityCombat, EVT_CastStart, this);
	m_wpHost->addRegEvt(Event::GID_EntityMovement, EVT_Move, this);
	m_wpHost->addRegEvt(Event::GID_EntityCombat, EVT_CastEnd, this);
	m_wpHost->addRegEvt(Event::GID_EntityCombat, EVT_TargetSelected, this);
	m_wpHost->addRegEvt(Event::GID_EntityCombat, EVT_CastPrep, this);
	m_wpHost->addRegEvt(Event::GID_EntityCombat, EVT_CastResult, this);


	m_nSR_SpellcastingBaseForCls		= getStaticData()->getData(1);
	m_nSR_SpellcastedBaseForCls			= getStaticData()->getData(2);
	m_nSR_SpellcastingBaseForElement	= getStaticData()->getData(3);
	m_nSR_SpellcastingBaseForUnderAttack = getStaticData()->getData(4);

	for (int i= Combat::CBS_Begin+1; i<Combat::CBS_End; i++)
	{
		if (m_theStates[i] != NULL)
		{
			m_theStates[i]->init(this, m_wpHost);
		}
	}

	setFlg(Combat::FIdx_CanBeTargeted);

	return true;
}

bool
CombatSysImp::onDetach()
{
	m_wpHost->rmvRegEvt(Event::GID_EntityCombat, EVT_CastStart, this);
	m_wpHost->rmvRegEvt(Event::GID_EntityMovement, EVT_Move, this);
	m_wpHost->rmvRegEvt(Event::GID_EntityCombat, EVT_CastEnd, this);
	m_wpHost->rmvRegEvt(Event::GID_EntityCombat, EVT_TargetSelected, this);
	m_wpHost->rmvRegEvt(Event::GID_EntityCombat, EVT_CastPrep, this);
	m_wpHost->rmvRegEvt(Event::GID_EntityCombat, EVT_CastResult, this);
	return true;
}


bool 
CombatSysImp::onUpdate(const int& nElapse)
{
	short nCurrentState = getState();
	if (nCurrentState==m_nPrevCbtSt)
	{
		return m_theStates[nCurrentState]->update(nElapse);
	}
	else
	{
#ifdef _DEBUG
		short nPrevState = m_nPrevCbtSt;
		LOG_DEBUG_V("CombatSysImp%s::onUpdate, from[%s]\n", getCombatTag(), getStateName(nPrevState));

		m_theStates[nPrevState]->leave();
		m_theStates[nCurrentState]->enter();
		m_nPrevCbtSt=nCurrentState;
		bool b = m_theStates[nCurrentState]->update(nElapse);

		LOG_DEBUG_V("CombatSysImp%s::onUpdate,   to[%s]\n", getCombatTag(), getStateName(nCurrentState));
		return b;
#else
		short nPrevState = m_nPrevCbtSt;
		m_theStates[nPrevState]->leave();
		m_theStates[nCurrentState]->enter();
		m_nPrevCbtSt=nCurrentState;
		return m_theStates[nCurrentState]->update(nElapse);
#endif
	}
}


void 
CombatSysImp::onEvent(IEvt& evt)
{
	m_theStates[getState()]->onEvent(evt);
}

void 
CombatSysImp::onEvent_CastPrep(IEvt& evt)
{
}

void 
CombatSysImp::onEvent_Cast(IEvt& evt)
{
	GECast* pGE = static_cast<GECast*>(&evt);

	updateMP( -pGE->m_wpSkill->m_nEnergeCost);

	m_wpCastingSkill = pGE->m_wpSkill;
	m_nTargetCC = pGE->m_wpTarget->getID();
	m_nSequenceCC = pGE->m_nSeqID;
}


void 
CombatSysImp::onEvent_Target(IEvt& evt)
{
}

void 
CombatSysImp::onEvent_CastResult(IEvt& evt)
{
	GECastResult* pGE = static_cast<GECastResult*>(&evt);

	if (pGE->m_theResult.m_nResult==CombatResult::Succeed)
	{
		if (pGE->m_wpSkill->IsNegative())
		{
			//
			//	the one who attack me will be my enemy , if i have no target enemy now
			if (!hasTargetCE())
			{
				setTargetCE(pGE->getID());
			}
		}
		//
		//	Effect of being hitted (including being healed)
		if (pGE->m_wpSkill->m_nHit_E)
		{
			EvtEntitySC evto(pGE->m_wpSkill->m_nHit_E, EvtEntitySC::op_DoIt, EvtEntitySC::IDT_Effect);
			m_wpHost->onEvent(evto);
		}
		else
		{
			EvtEntitySC evto(m_nSR_SpellcastingBaseForUnderAttack);
			m_wpHost->onEvent(evto);
		}

		if (pGE->m_theResult.m_nHPAffected)
		{
			updateHP( pGE->m_theResult.m_nHPAffected);
		}
	}
	else
	{
		EvtEntityPC evtx(EvtEntityPC::eProp_CombatStatus, 0, pGE->m_theResult.m_nResult);
		m_wpHost->onEvent(evtx);
	}
}

bool
CombatSysImp::hasSkill(short& nStyle)const
{
	if (m_wpCastingSkill)
	{
		nStyle = m_wpCastingSkill->m_nStyle;
		return true;
	}
	else
	{
		return false;
	}
}

bool
CombatSysImp::hasSequence(const short& nSeq)const
{
	if (m_wpCastingSkill)
	{
		return m_nSequenceCC == nSeq;
	}
	else
	{
		return false;
	}
}

bool 
CombatSysImp::EffectUpdate_CastStart(int& nTTL)
{
	if (m_wpCastingSkill)
	{
		//
		//	Spell Casting Animation Being Override
		if (m_wpCastingSkill->m_nCasting_A)
		{
			EvtEntitySC evtx(m_wpCastingSkill->m_nCasting_A, EvtEntitySC::op_DoIt, EvtEntitySC::IDT_Animation);
			m_wpHost->onEvent(evtx);
		}
		else
		{
			EvtEntitySC evtx(m_wpCastingSkill->m_nClass + m_nSR_SpellcastingBaseForCls);
			m_wpHost->onEvent(evtx);
		}

		//
		//	Spell Casting Effect Being Override
		if (m_wpCastingSkill->m_nCasting_E)
		{
			EvtEntitySC evty(m_wpCastingSkill->m_nCasting_E, EvtEntitySC::op_DoIt, EvtEntitySC::IDT_Effect);
			m_wpHost->onEvent(evty);
		}
		else
		{
			EvtEntitySC evty(m_wpCastingSkill->m_nElement + m_nSR_SpellcastingBaseForElement);
			m_wpHost->onEvent(evty);
		}

		nTTL = m_wpCastingSkill->m_nCastTime;
		return true;
	}
	else
	{
		nTTL = 0;
		return false;
	}
}

bool 
CombatSysImp::EffectUpdate_CastCancel()
{
	if (m_wpCastingSkill)
	{
		//
		//	Spell Casting Animation Being Override
		if (m_wpCastingSkill->m_nCasting_A)
		{
			EvtEntitySC evtx(m_wpCastingSkill->m_nCasting_A, EvtEntitySC::op_StopIt, EvtEntitySC::IDT_Animation);
			m_wpHost->onEvent(evtx);
		}
		else
		{
			EvtEntitySC evtx(m_wpCastingSkill->m_nClass + m_nSR_SpellcastingBaseForCls, EvtEntitySC::op_StopIt);
			m_wpHost->onEvent(evtx);
		}

		//
		//	Spell Casting Effect Being Override
		/*if (m_wpCastingSkill->m_nCasting_E)
		{
		EvtEntitySC evty(m_wpCastingSkill->m_nCasting_E, EvtEntitySC::op_StopIt, EvtEntitySC::IDT_Effect));
		m_wpHost->onEvent(evty);
		}
		else
		{
		EvtEntitySC evty(m_wpCastingSkill->m_nElement + m_nSR_SpellcastingBaseForElement, EvtEntitySC::op_StopIt);
		m_wpHost->onEvent(evty);
		}*/

		m_wpCastingSkill = NULL;
		return true;
	}
	else
	{
		return false;
	}
}

bool
CombatSysImp::EffectUpdate_CastEnding()
{
	if (m_wpCastingSkill)
	{
		return sendMsg_CbtEnd();
	}

	return false;
}

bool 
CombatSysImp::EffectUpdate_CastEnded()
{
	if (m_wpCastingSkill)
	{
		if (m_wpCastingSkill->m_nCasted_A)
		{
			EvtEntitySC evt(m_wpCastingSkill->m_nCasted_A, EvtEntitySC::op_DoIt, EvtEntitySC::IDT_Animation);
			m_wpHost->onEvent(evt);
		}
		else
		{
			EvtEntitySC evt(m_wpCastingSkill->m_nClass + m_nSR_SpellcastedBaseForCls);
			m_wpHost->onEvent(evt);
		}


		if (m_wpCastingSkill->m_nCasted_E)
		{
			EvtEntitySC evt(m_wpCastingSkill->m_nCasted_E, EvtEntitySC::op_DoIt, EvtEntitySC::IDT_Animation);
			m_wpHost->onEvent(evt);
		}


		//
		//	Clear current casting spell
		//
		m_wpCastingSkill = NULL;
	}
	return true;
}

IEntity*
CombatSysImp::getTarget(const int& nTargetID /* = 0 */)
{
	return m_wpHost->getScene()->getEntityMgr()->getEntity(nTargetID);
}

IEntity*
CombatSysImp::getTargetCC()
{
	IEntity* pTarget = NULL;
	if (m_nTargetCC!=0)
	{
		pTarget = m_wpHost->getScene()->getEntityMgr()->getEntity(m_nTargetCC);
	}

	if (pTarget)
	{
		return pTarget;
	}
	else
	{
		//
		//	Target of current casting lost
		m_nTargetCC = 0;
		m_wpCastingSkill = NULL;
		return NULL;
	}
}

IEntity*
CombatSysImp::getTargetCE()
{
	IEntity* pTarget = NULL;
	if (m_nTargetCE!=0)
	{
		pTarget = m_wpHost->getScene()->getEntityMgr()->getEntity(m_nTargetCE);
	}

	if (pTarget)
	{
		//	
		//	Current Enemy Target:
		//	Should be a combat unit
		//	Should be in CanbeTarget State
		//	Should be in different faction
		ICombatSys* pCombat = pTarget->getCombatSys();
		if (pCombat &&
			pCombat->hasFlg(Combat::FIdx_CanBeTargeted) &&		
			pCombat->getFaction()!=getFaction())
		{
			return pTarget;
		}
	}

	//
	//	Target of current enemy lost
	m_nTargetCE = 0;
	return NULL;
}

bool 
CombatSysImp::initState(short nST, ICombatState* pState)
{
	if (pState!=NULL && 
		nST>Combat::CBS_Begin && 
		nST<Combat::CBS_End)
	{
		if (m_theStates[nST])
		{
			delete m_theStates[nST];
		}

		m_theStates[nST] = pState;

		if (m_wpHost)
		{
			pState->init(this, m_wpHost);
		}
		return true;
	}
	else
	{
		delete pState;
		return false;
	}
}